package br.ufc.si.tcc.qmusic.util;

import java.io.*;
import java.net.URL;

// Pacotes de extensão de Java
import javax.sound.midi.*;

public class MidiData {

    private Track track;
    // reprodutor para seqüências MIDI
    private Soundbank soundBank;
    private Synthesizer synthesizer;
    private Sequencer sequencer;
    // seqüência MIDI
    private Sequence sequence;
    // eventos MIDI contendo tempo e MidiMessages
    private MidiEvent currentEvent, nextEvent;
    // mensagem MIDI usualmente contendo mensagens de som
    private ShortMessage noteMessage;
    // mensagens MIDI short, meta ou sysex
    private MidiMessage message;
    // índice do evento MIDI na trilha, comando em uma mensagem MIDI
    private int eventIndex = 0;
    private int command;
    private static MidiData midiData;

    public static MidiData getNewInstance() {

        if (midiData == null) {
            return new MidiData();
        }
        return midiData;
    }

    // método para reproduzir seqüência MIDI através de um seqüenciador
    public void play() {
        // inicia seqüenciador-padrão
        try {
            // obtém seqüenciador de MidiSystem
            sequencer = MidiSystem.getSequencer();

            // abre recursos do seqüenciador
            sequencer.open();

            // carrega MIDI no seqüenciador
            sequencer.setSequence(sequence);

            // reproduz seqüência
            sequencer.start();

        } // erro de disponibilidade de recurso MIDI
        catch (MidiUnavailableException noMidiException) {
            noMidiException.printStackTrace();
        } // encontrado arquivo MIDI corrompido ou inválido
        catch (InvalidMidiDataException badMidiException) {
            badMidiException.printStackTrace();

        }

    } // fim do método play

    // método que devolve tempo/resolução ajustados do MIDI
    public int getResolution() {
        return 500 / sequence.getResolution();
    }

    // obtém MIDI e prepara trilha em MIDI para ser acessada
    public boolean initialize(File file) {
        // coloca MIDI válido do arquivo na seqüência
        try {
            
            sequence = MidiSystem.getSequence(file);
            synthesizer = MidiSystem.getSynthesizer();            
            //soundBank = MidiSystem.getSoundbank(file);

        } // arquivo MIDI ilegível ou MIDI não-suportado
        catch (InvalidMidiDataException badMIDI) {
            badMIDI.printStackTrace();
            return false;
        } catch (MidiUnavailableException badMIDI) {
            badMIDI.printStackTrace();
            return false;
        } catch (IOException ioException) {
            ioException.printStackTrace();
            return false;
        }

        return true;

    } // fim do método initialize

    // prepara trilha mais longa para ser lida e obtém primeiro evento MIDI
    public boolean initializeTrack() {
        // obtém todas as trilhas da seqüência
        Track tracks[] = sequence.getTracks();

        if (tracks.length == 0) {
            System.err.println("No tracks in MIDI sequence!");
            return false;
        }
        track = tracks[ 0];

        // encontra trilha mais longa
        for (int i = 0; i < tracks.length; i++) {
            if (tracks[ i].size() > track.size()) {
                track = tracks[ i];
            }
        }
        // configura evento MIDI corrente para primeiro evento na trilha
        currentEvent = track.get(eventIndex);
        // obtém menssagem MIDI do evento
        message = currentEvent.getMessage();

        // inicialização de trilha bem sucedida
        return true;

    } // fim do método initializeTrack

    // prossegue para próximo evento na trilha
    public void goNextEvent() {
        eventIndex++;
        currentEvent = track.get(eventIndex);
        message = currentEvent.getMessage();
    }
    // obtém intervalo de tempo entre eventos

    public int getEventDelay() {
        // o primeiro intervalo de tempo do evento é a sua duração
        if (eventIndex == 0) {
            return (int) currentEvent.getTick();
        }
        // diferença de tempo entre evento corrente e o próximo
        return (int) (track.get(eventIndex + 1).getTick() - currentEvent.getTick());


    }

    // retorna se a trilha terminou
    public boolean isTrackEnd() {
        // se eventIndex é menor do que o número de eventos da trilha
        if (eventIndex + 1 < track.size()) {
            return false;
        }

        return true;
    }

    // obtém comando ShortMessage corrente do evento
    public int getEventCommand() {
        if (message instanceof ShortMessage) {
            // obtém MidiMessage para fins de acesso
            noteMessage = (ShortMessage) message;
            return noteMessage.getCommand();
        }

        return -1;
    }

    // obtém o número da nota do evento corrente
    public int getNote() {
        if (noteMessage != null) {
            return noteMessage.getData1();
        }

        return -1;
    }

// obtém o volume do evento corrente
    public int getVolume() {
        return noteMessage.getData2();

    }

    public Synthesizer getSynthensizer() {
        return this.synthesizer;
    }

    public int getChannel() {
        return noteMessage.getChannel();

    }

    public Soundbank getSoundBank() {
        return soundBank;
    }
    
    
}